home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
HPAVC
/
HPAVC CD-ROM.iso
/
HPACK78S.ZIP
/
frontend.c
< prev
next >
Wrap
C/C++ Source or Header
|
1992-12-03
|
38KB
|
1,142 lines
/****************************************************************************
* *
* HPACK Multi-System Archiver *
* =========================== *
* *
* Main Archiver Code *
* FRONTEND.C Updated 28/08/92 *
* *
* This program is protected by copyright and as such any use or copying of *
* this code for your own purposes directly or indirectly is highly uncool *
* and if you do so there will be....trubble. *
* And remember: We know where your kids go to school. *
* *
* Copyright 1989 - 1992 Peter C.Gutmann. All rights reserved *
* *
****************************************************************************/
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#ifdef __MAC__
#include "defs.h"
#include "arcdir.h"
#include "choice.h"
#include "error.h"
#include "filesys.h"
#include "flags.h"
#include "frontend.h"
#include "hpacklib.h"
#include "hpaktext.h"
#include "system.h"
#include "tags.h"
#include "wildcard.h"
#include "crypt.h"
#include "fastio.h"
#include "hpackio.h"
#include "store.h"
#else
#include "defs.h"
#include "arcdir.h"
#include "choice.h"
#include "error.h"
#include "filesys.h"
#include "flags.h"
#include "frontend.h"
#include "hpacklib.h"
#include "system.h"
#include "tags.h"
#include "wildcard.h"
#include "crypt/crypt.h"
#include "io/fastio.h"
#include "io/hpackio.h"
#include "language/hpaktext.h"
#include "store/store.h"
#endif /* __MAC__ */
/* Prototypes for functions in VIEWFILE.C */
void listFiles( void );
/* Prototypes for functions in ARCHIVE.C */
void addData( const char *filePath, const FILEINFO *fileInfoPtr, const WORD dirIndex, const FD dataFD );
void retrieveData( FILEHDRLIST *fileInfo );
BOOLEAN confirmSkip( const char *str1, const char *str2, const BOOLEAN str1fileName );
void initBadFileInfo( void );
void processBadFileInfo( void );
#ifdef __AMIGA__
/* Prototypes for functions defined in AMIGA.C */
LONG storeDiskObject( const char *pathName, const FD outFD );
void storeComment( const FD outFD );
#endif /* __AMIGA__ */
#ifdef __OS2__
/* Prototypes for functions in OS2.C */
LONG storeEAinfo( const BOOLEAN isFile, const char *pathName, const LONG eaSize, const FD archiveFD );
#endif /* __OS2__ */
/* The following are defined in ARCHIVE.C */
extern BOOLEAN overWriteEntry; /* Whether we overwrite the existing file
entry or add a new one in addFileHeader()
- used by FRESHEN, REPLACE, UPDATE */
/* The following are defined in ARCDIRIO.C */
extern int cryptFileDataLength; /* The size of the encryption header at the
start of the compressed data */
/* The extensions used for HPACK archives, and used to match HPACK archives */
#if defined( __ATARI__ ) || defined( __MSDOS__ )
char HPAK_EXT[] = ".HPK";
#elif defined( __ARC__ )
char HPAK_EXT[] = "";
#else
char HPAK_EXT[] = ".hpk";
#endif /* __MSDOS__ */
#ifdef __ARC__
char HPAK_MATCH_EXT[] = "";
#else
char HPAK_MATCH_EXT[] = ".\04Hh\06\04Pp\06\04Kk\06"; /* Compiled ".[Hh][Pp][Kk]" */
#endif /* __ARC__ */
/* The distance in bytes to the next piece of data to handle */
long skipDist;
/* The name of the archive currently being processed */
char archiveFileName[ MAX_PATH ];
/* Some general vars used throughout HPACK */
char choice; /* The command to the archiver */
WORD flags = 0; /* Various flags set by the user */
WORD dirFlags = 0; /* Directory-handling flags set by the user */
WORD overwriteFlags = 0; /* Overwrite-on-extract flags */
WORD viewFlags = 0; /* Options for the View command */
WORD xlateFlags = 0; /* Options for output translation */
WORD cryptFlags = 0; /* Options for encryption/security */
WORD multipartFlags = 0; /* Options for multipart read/write control */
WORD sysSpecFlags = 0; /* System-specific options */
WORD commentType = TYPE_NORMAL; /* The type of the archive comment */
BOOLEAN archiveChanged = FALSE; /* Whether the archive has been changed */
BOOLEAN firstFile = TRUE; /* Whether this is first file we're processing */
char basePath[ MAX_PATH ]; /* The output directory given by the -b option */
int basePathLen = 0; /* The length of the basePath */
int screenHeight, screenWidth; /* The screen size */
char *signerID; /* userID of archive signer */
char *mainUserID = NULL; /* userID of main PKE destination (needs to be init'd) */
char *secUserID; /* userID of secondary PKE destination */
/****************************************************************************
* *
* Support Routines for the Main Code *
* *
****************************************************************************/
/* Keep a record of every archive processed and don't do the same archive
again. This detail is necessary due to a combination of local OS file
handling and HPACK file handling. In the cases where HPACK works on
a second file which it then renames as the original after deleting the
original, the findNext() call may then match this newly-created archive
and process it again, which results in either a "Nothing to do" error
(for DELETE, FRESHEN, and UPDATE), or an endless loop as we keep matching
the new archives (for REPLACE) */
typedef struct FL {
char *name; /* This archive's name */
struct FL *next; /* Pointer to next record */
} FILEDATA;
static FILEDATA *archiveNameListHead = NULL;
BOOLEAN addArchiveName( char *archiveName )
{
FILEDATA *namePtr, *prevPtr, *newRecord;
for( namePtr = archiveNameListHead; namePtr != NULL; \
prevPtr = namePtr, namePtr = namePtr->next )
/* Check whether we've already processed an archive of this name */
if( !strcmp( archiveName, namePtr->name ) )
return( TRUE );
/* We have reached the end of the list without a match, so we add this
archive name to the list */
if( ( newRecord = ( FILEDATA * ) hmalloc( sizeof( FILEDATA ) ) ) == NULL || \
( newRecord->name = ( char * ) hmalloc( strlen( archiveName ) + 1 ) ) == NULL )
error( OUT_OF_MEMORY );
strcpy( newRecord->name, archiveName );
newRecord->next = NULL;
if( archiveNameListHead == NULL )
archiveNameListHead = newRecord;
else
prevPtr->next = newRecord;
return( FALSE );
}
#if !defined( __MSDOS__ ) || defined( GUI )
/* Free up the memory used by the list of archive names */
void freeArchiveNames( void )
{
FILEDATA *namePtr = archiveNameListHead, *headerPtr;
/* A simpler implementation of the following would be:
for( namePtr = archiveNameListHead; namePtr != NULL; namePtr = namePtr->next )
hfree( namePtr );
However this will fail on some systems since we will be trying to
read namePtr->next out of a record we have just free()'d */
while( namePtr != NULL )
{
headerPtr = namePtr;
namePtr = namePtr->next;
hfree( headerPtr->name );
hfree( headerPtr );
}
}
#endif /* !__MSDOS__ || GUI */
/* Keep a record of every file added to an archive. This is used by the
move option, which is a two-pass operation: The first pass adds the files
to the archive, the second pass goes through and deletes the added files */
static FILEDATA *filePathListHead = NULL, *prevPtr;
static void addFilePath( char *filePath )
{
FILEDATA *newRecord;
/* Add the filepath to the list of filepaths */
if( ( newRecord = ( FILEDATA * ) hmalloc( sizeof( FILEDATA ) ) ) == NULL || \
( newRecord->name = ( char * ) hmalloc( strlen( filePath ) + 1 ) ) == NULL )
error( OUT_OF_MEMORY );
strcpy( newRecord->name, filePath );
newRecord->next = NULL;
if( filePathListHead == NULL )
filePathListHead = newRecord;
else
prevPtr->next = newRecord;
prevPtr = newRecord;
}
/* Delete files and free up the memory used by the list of file paths */
void wipeFilePaths( void )
{
FILEDATA *namePtr = filePathListHead, *headerPtr;
/* Step through the list of names freeing them */
while( namePtr != NULL )
{
headerPtr = namePtr;
namePtr = namePtr->next;
/* Wipe the file */
wipeFile( headerPtr->name );
/* Free the memory */
hfree( headerPtr->name );
hfree( headerPtr );
}
}
/* Add a series of directories to an archive (used by processDir()) */
static void addDirPath( char *fullPath, char *internalPath, const int currPathLen, const WORD dirIndex )
{
FILEINFO fileInfo;
int matchLen, count;
char ch;
/* We shouldn't get here if the DIR_NOCREATE flags is set to specify that
no directories should be created if they don't already exist */
if( dirFlags & DIR_NOCREATE )
{
fullPath[ currPathLen ] = '\0'; /* Truncate to pure path */
error( PATH_NOT_FOUND, internalPath );
}
setArchiveCwd( dirIndex );
/* Grovel through all directories in the path, adding them to the
archive directory tree as we go */
matchLen = strlen( getPath( dirIndex ) );
while( matchLen < currPathLen )
{
/* Get the next directories name */
for( count = matchLen + 1; internalPath[ count ] && internalPath[ count ] != SLASH;
count++ );
ch = internalPath[ count ];
internalPath[ count ] = '\0';
/* Try and add the new directory */
if( findFirst( fullPath, ALLFILES_DIRS, &fileInfo ) && isDirectory( fileInfo ) )
{
#ifndef GUI
hprintfs( MESG_ADDING_DIRECTORY_s, fileInfo.fName );
#endif /* !GUI */
addDirName( fileInfo.fName, fileInfo.fTime );
if( flags & STORE_ATTR )
{
#if defined( __MSDOS__ )
addDirData( TAG_MSDOS_ATTR, TAGFORMAT_STORED, LEN_MSDOS_ATTR, LEN_NONE );
fputDirByte( fileInfo.fAttr ); /* Save attributes */
#elif defined( __AMIGA__ )
addDirData( TAG_AMIGA_ATTR, TAGFORMAT_STORED, LEN_AMIGA_ATTR, LEN_NONE );
fputDirByte( fileInfo.fAttr ); /* Save attributes */
if( fileInfo.hasComment )
storeComment( 0 ); /* Save comment */
storeDiskObject( fullPath, 0 );
#elif defined( __ARC__ )
addDirData( TAG_ARC_ATTR, TAGFORMAT_STORED, LEN_ARC_ATTR, LEN_NONE );
fputDirByte( fileInfo.fAttr ); /* Save attributes */
#elif defined( __ATARI__ )
addDirData( TAG_ATARI_ATTR, TAGFORMAT_STORED, LEN_ATARI_ATTR, LEN_NONE );
fputDirByte( fileInfo.fAttr ); /* Save attributes */
#elif defined( __MAC__ )
addDirData( TAG_MAC_ATTR, TAGFORMAT_STORED, LEN_MAC_ATTR, LEN_NONE );
fputDirWord( fileInfo.fAttr ); /* Save attributes */
addDirData( TAG_CREATION_TIME, TAGFORMAT_STORED, LEN_CREATION_TIME, LEN_NONE );
fputDirLong( fileInfo.createTime ); /* Save creation time */
if( fileInfo.backupTime )
{
/* Save backup time if there is one */
addDirData( TAG_BACKUP_TIME, TAGFORMAT_STORED, LEN_BACKUP_TIME, LEN_NONE );
fputDirLong( fileInfo.backupTime );
}
if( fileInfo.versionNumber )
{
/* Save version number if there is one */
addDirData( TAG_VERSION_NUMBER, TAGFORMAT_STORED, LEN_VERSION_NUMBER, LEN_NONE );
fputDirWord( ( WORD ) fileInfo.versionNumber );
}
#elif defined( __OS2__ )
if( fileInfo.aTime )
{
/* Save access time if there is one */
addDirData( TAG_ACCESS_TIME, TAGFORMAT_STORED, LEN_ACCESS_TIME, LEN_NONE );
fputDirLong( fileInfo.aTime );
}
if( fileInfo.cTime )
{
/* Save creation time if there is one */
addDirData( TAG_CREATION_TIME, TAGFORMAT_STORED, LEN_CREATION_TIME, LEN_NONE );
fputDirLong( fileInfo.cTime );
}
storeEAinfo( FALSE, fullPath, fileInfo.eaSize, archiveFD );
#elif defined( __UNIX__ )
addDirData( TAG_UNIX_ATTR, TAGFORMAT_STORED, LEN_UNIX_ATTR, LEN_NONE );
fputDirWord( fileInfo.statInfo.st_mode ); /* Save attributes */
addDirData( TAG_ACCESS_TIME, TAGFORMAT_STORED, LEN_ACCESS_TIME, LEN_NONE );
fputDirLong( fileInfo.statInfo.st_atime ); /* Save access time */
#endif /* Various OS-dependant attribute writes */
}
internalPath[ count ] = ch;
matchLen = count;
findEnd( &fileInfo );
}
else
error( PATH_NOT_FOUND, internalPath );
}
}
/* Process one directories worth of files */
#define MAX_LEVELS 15 /* Max.directory depth we will recurse to */
static void processDir( FILENAMEINFO *fileNameList, const char *filePath, \
const int internalPathStart )
{
FILEINFO fileInfo, currentDir[ MAX_LEVELS ];
FILENAMEINFO *fileNamePtr;
int currentLevel = 0, i;
int pathEnd[ MAX_LEVELS ];
char fullPath[ MAX_PATH ], *internalPath, ch;
int currPathLen = strlen( filePath ), internalPathLen, nameLen;
BOOLEAN foundFile, skipFile, skipFind;
/* BOOLEAN recurseFlags[ MAX_LEVELS ], forceRecurseFlags[ MAX_LEVELS ]; */
/* BOOLEAN doRecurse = ( flags & RECURSE_SUBDIR ) ? TRUE : FALSE; */
/* BOOLEAN recurseOnce = FALSE, forceRecurse = FALSE; */
WORD dirIndex = ROOT_DIR; /* Default dir for no STORE_PATH option */
ATTR matchAttr = ( flags & STORE_ATTR ) ? ALLFILES_DIRS : FILES_DIRS;
FD workFD, savedInFD;
/* Perform a depth-first search of all directories below the current
one. Note that there is little advantage to be gained from an
explicit match rather than MATCH_ALL since we will at some point need
to match MATCH_ALL anyway to find directories; this also lets us use
extended wildcards when doing the string comparisons */
strcpy( fullPath, filePath );
internalPath = fullPath + internalPathStart;
internalPathLen = currPathLen - internalPathStart;
if( internalPathLen && !( internalPathLen == 1 && *internalPath == SLASH ) )
/* Append SLASH if necessary */
fullPath[ currPathLen++ ] = SLASH;
strcpy( fullPath + currPathLen, MATCH_ALL );
foundFile = skipFind = findFirst( fullPath, matchAttr, &fileInfo );
while( TRUE )
if( foundFile && ( skipFind || findNext( &fileInfo ) ) )
{
foundFile = TRUE;
skipFind = FALSE;
/* See if the file we have found is a directory */
if( isDirectory( fileInfo ) )
{
#if defined( __AMIGA__ ) || defined( __ATARI__ ) || defined( __MSDOS__ ) || \
defined( __OS2__ ) || defined( __UNIX__ )
/* Continue if we've hit "." or ".." */
if( *fileInfo.fName == '.' && ( !fileInfo.fName[ 1 ] || \
( fileInfo.fName[ 1 ] == '.' && !fileInfo.fName[ 2 ] ) ) )
continue;
#endif /* __AMIGA__ || __ATARI__ || __MSDOS__ || __OS2__ || __UNIX__ */
/* if( recurseOnce || doRecurse ) */
if( flags & RECURSE_SUBDIR )
{
/* Check the path length (the +5 is for the later
addition of SLASH between the name and the path, and
a SLASH_MATCH_ALL at the end) */
if( ( ( nameLen = strlen( fileInfo.fName ) ) + currPathLen + 4 ) >= MAX_PATH )
error( PATH_s_s_TOO_LONG, fullPath, fileInfo.fName );
/* Set up the information for the new directory */
/* recurseFlags[ currentLevel ] = doRecurse; */
/* recurseOnce = FALSE; */
pathEnd[ currentLevel ] = currPathLen;
currentDir[ currentLevel++ ] = fileInfo;
strcpy( fullPath + currPathLen, fileInfo.fName );
currPathLen += nameLen;
if( flags & STORE_PATH )
{
/* Add the new directory if it isn't already in the archive */
if( matchPath( internalPath, currPathLen - internalPathStart, \
&dirIndex ) != PATH_FULLMATCH )
{
/* Only add this path if we've been asked to add
all paths anyway regardless of whether they contain
any files */
if( dirFlags & DIR_ALLPATHS )
{
addDirPath( fullPath, internalPath, \
currPathLen - internalPathStart, dirIndex );
archiveChanged = TRUE;
}
}
else
{
/* Go to that directory and update its timestamp */
setArchiveCwd( dirIndex );
setDirTime( dirIndex, fileInfo.fTime );
}
}
#ifndef GUI
hprintfs( MESG_CHECKING_DIRECTORY_s, internalPath );
#endif /* !GUI */
if( currentLevel >= MAX_LEVELS )
error( TOO_MANY_LEVELS_NESTING );
strcpy( fullPath + currPathLen++, SLASH_MATCH_ALL );
foundFile = skipFind = findFirst( fullPath, matchAttr, &fileInfo );
continue;
}
else
/* Make sure we don't mistake directories for filenames */
continue;
}
/* Try and match the filename */
internalPathLen = currPathLen - internalPathStart;
for( fileNamePtr = fileNameList; fileNamePtr != NULL; \
fileNamePtr = fileNamePtr->next )
/* if( ( forceRecurse && !isDirectory( fileInfo ) ) || \ */
if( matchString( fileNamePtr->fileName, fileInfo.fName, fileNamePtr->hasWildcards ) )
if( !( flags & STORE_PATH ) || \
( matchPath( internalPath, ( internalPathLen ) ? internalPathLen - 1 : 0, \
&dirIndex ) ) == PATH_FULLMATCH )
{
/* \* Check whether what we've found is a directory */
/* if( isDirectory( fileInfo ) ) */
/* { */
/* \* We've matched a directory, add the directory */
/* info and recurse through it */
/* skipFind = TRUE; */
/* recurseOnce = forceRecurse = TRUE; */
/* break; /* Exit for loop */
/* } */
retry:
strcpy( fullPath + currPathLen, fileInfo.fName );
/* Make sure we don't try to archive the archive */
skipFile = FALSE;
if( ( ( i = strlen( fileInfo.fName ) - 3 ) > 1 ) && \
( ( ch = fileInfo.fName[ i ] ) == '$' || \
ch == 'H' || ch == 'h' ) )
{
/* First we perform a relatively cheap search
for the HPAK_MATCH_EXT or "$$x" suffix to see
if this is actually an archive file. Only
then will it be necessary to perform the
expensive call to check if the files are identical */
if( matchString( HPAK_MATCH_EXT, fileInfo.fName + i - 1, HAS_WILDCARDS ) || \
!strncmp( fileInfo.fName + i, "$$", 2 ) )
{
/* We've found an HPACK-related file; check
them to see if they are identical. The
following [ i + 2 ] is safe since we can
only arrive at this point if there is a
valid suffix at [ i + 2 ] */
switch( fileInfo.fName[ i + 2 ] )
{
case '1':
/* Extension '.$$1' */
if( errorFD != IO_ERROR )
skipFile = isSameFile( errorFileName, fullPath );
break;
case '2':
/* Extension '.$$2' */
if( dirFileFD != IO_ERROR )
skipFile = isSameFile( dirFileName, fullPath );
break;
case '3':
/* Extension '.$$3' */
if( secFileFD != IO_ERROR )
skipFile = isSameFile( secFileName, fullPath );
break;
default:
/* Extension '.HPK' */
skipFile = isSameFile( archiveFileName, fullPath );
}
}
}
if( skipFile )
/* The file is an HPACK-related one, skip it */
break;
/* Complain if the file is already in the archive and we
try to ADD it. Don't complain otherwise, since it will
be replaced during pass 2 of the UPDATE command */
if( addFileName( ( flags & STORE_PATH ) ? dirIndex : 0, \
commentType, fileInfo.fName ) == TRUE )
{
if( choice == ADD )
#ifdef GUI
alert( ALERT_FILE_IN_ARCH, fileInfo.fName );
#else
hprintf( MESG_FILE_s_ALREADY_IN_ARCH__SKIPPING, \
fileInfo.fName );
#endif /* GUI */
break; /* Exit the for loop */
}
/* Ask the user whether they want to handle this file */
if( ( flags & INTERACTIVE ) && \
confirmSkip( MESG_ADD, fileInfo.fName, FALSE ) )
{
/* Remove the last filename entered from the name
table since we since we won't be adding this file */
deleteLastFileName();
break; /* Exit the for loop */
}
if( ( workFD = hopen( fullPath, O_RDONLY | S_DENYWR | A_SEQ ) ) == IO_ERROR )
#ifdef GUI
alert( ALERT_CANNOT_OPEN_DATAFILE, internalPath );
#else
hprintf( WARN_CANNOT_OPEN_DATAFILE_s, internalPath );
#endif /* GUI */
else
{
savedInFD = getInputFD();
setInputFD( workFD );
addData( fullPath, &fileInfo, dirIndex, workFD );
hclose( workFD );
setInputFD( savedInFD );
/* If we're moving file, remember the name so we
can go back and delete it later */
if( flags & MOVE_FILES )
addFilePath( fullPath );
}
break; /* Exit the for loop */
}
else
{
/* Build the path to the file inside the archive. The
currPathLen check is in case of a trailing SLASH */
addDirPath( fullPath, internalPath, ( internalPathLen ) ? \
internalPathLen - 1 : 0, dirIndex );
/* Restore the directory index and continue */
dirIndex = currEntry;
goto retry;
}
}
else
/* Go back up one level if we can and try again */
if( currentLevel )
{
fullPath[ currPathLen ? currPathLen - 1 : 0 ] = '\0';
#ifndef GUI
hprintfs( MESG_LEAVING_DIRECTORY_s, internalPath );
#endif /* !GUI */
findEnd( &fileInfo );
fileInfo = currentDir[ --currentLevel ];
currPathLen = pathEnd[ currentLevel ];
/* forceRecurse = FALSE; */
/* doRecurse = recurseFlags[ currentLevel ]; */
foundFile = TRUE;
if( flags & STORE_PATH )
popDir();
}
else
break;
}
/* Process a data file (with wildcards allowed) */
void handleArchive( void )
{
FD workFD, freshenFD;
FILEHDRLIST *prevPtr, *endPtr = NULL;
DIRHDRLIST *dirInfoPtr;
FILEINFO fileInfo;
FILEPATHINFO *pathInfoPtr;
FILENAMEINFO *fileInfoPtr;
char externalPath[ MAX_PATH ], *filePath, *fileName, *path;
FILEHDRLIST *list2currPtr, *list2startPtr = NULL;
int dummy;
BOOLEAN falseBasePath = FALSE, hasMatch, processedFile;
WORD dirNo;
/* Reset the fast I/O subsystem */
resetFastOut();
/* Make sure the base path is valid */
if( *basePath && !dirExists( basePath ) )
error( CANNOT_ACCESS_BASEDIR, basePath );
/* Options which add files to an archive */
if( choice == ADD || choice == UPDATE )
{
/* Write ID string at start if necessary */
if( !htell( archiveFD ) )
{
memcpy( _outBuffer, HPACK_ID, HPACK_ID_SIZE );
_outByteCount = HPACK_ID_SIZE;
}
/* Write encryption information */
if( cryptFlags & ( CRYPT_CKE_ALL | CRYPT_PKE_ALL ) )
cryptFileDataLength = cryptBegin( ( cryptFlags & CRYPT_SEC ) ? \
SECONDARY_KEY : MAIN_KEY );
else
cryptFileDataLength = 0;
/* Add the external path component if there is one. Note that
basePath already ends in a SLASH */
if( *basePath )
strcpy( externalPath, basePath );
/* Process each group of specified file in subdirectories seperately.
We need to do things in this somewhat messy manner instead of
just passing <path>/<name> to findFirst in case we are asked to
do subdirectories as well */
for( pathInfoPtr = filePathListStart; pathInfoPtr != NULL; \
pathInfoPtr = pathInfoPtr->next )
{
/* Add the internal path component */
strcpy( externalPath + basePathLen, pathInfoPtr->filePath );
#ifdef __VMS__
/* If there is a node name attached to the path, make sure we
don't treat it as part of the file path */
if( pathInfoPtr->node != NULL )
processDir( pathInfoPtr->fileNames, externalPath, \
basePathLen + strlen( pathInfoPtr->node ) + \
( ( pathInfoPtr->device != NULL ) ? \
strlen( pathInfoPtr->device ) : 0 ) );
else
#endif /* __VMS__ */
/* If there is a device attached to the path, make sure we
don't treat it as part of the file path */
processDir( pathInfoPtr->fileNames, externalPath, \
basePathLen + ( ( pathInfoPtr->device != NULL ) ? \
strlen( pathInfoPtr->device ) : 0 ) );
}
if( cryptFlags & ( CRYPT_CKE_ALL | CRYPT_PKE_ALL ) )
cryptEnd();
/* Write the archive directory and reset the error status if necessary */
if( archiveChanged )
{
writeArcDir();
errorFD = IO_ERROR;
oldArcEnd = 0;
}
return;
}
/* Options which extract/change files in an archive */
if( choice == DELETE || choice == FRESHEN || choice == REPLACE )
{
/* Open a destination archive file. Note that we can't use tmpnam()
since that would open a file in the current directory, which may
or may not be the same as the archive directory. For the same
reason we can't make use of a preset directory for storing temporary
files (such as /tmp or getenv( "TMP" ) ). So we open a file of the
form "archiveFileName." + TEMP_EXT */
strcpy( errorFileName, archiveFileName );
strcpy( errorFileName + strlen( errorFileName ) - 3, TEMP_EXT );
if( ( workFD = hcreat( errorFileName, CREAT_ATTR ) ) == IO_ERROR )
error( CANNOT_OPEN_TEMPFILE );
memcpy( _outBuffer, HPACK_ID, HPACK_ID_SIZE ); /* Put ID at start */
_outByteCount += HPACK_ID_SIZE;
errorFD = workFD;
setOutputFD( workFD );
}
else
if( choice == EXTRACT || choice == TEST )
/* Set up info for summary of files which didn't extract/test
properly */
initBadFileInfo();
/* Select files and directories to process */
fileHdrCurrPtr = fileHdrStartPtr;
while( fileHdrCurrPtr != NULL )
{
/* Get all the info we need about the filename and path */
filePath = getPath( fileHdrCurrPtr->data.dirIndex );
fileName = fileHdrCurrPtr->fileName;
dirNo = fileHdrCurrPtr->data.dirIndex;
if( dirNo != ROOT_DIR )
/* Stomp final SLASH on non-root dir. */
filePath[ strlen( filePath ) - 1 ] = '\0';
/* Try and match the filename and path against a command-line arg */
for( pathInfoPtr = filePathListStart; pathInfoPtr != NULL; \
pathInfoPtr = pathInfoPtr->next )
{
/* Get path information, skipping the initial SLASH if necessary */
path = pathInfoPtr->filePath;
if( *path == SLASH )
path++;
/* Try and match the filename and path against a given filespec */
for( fileInfoPtr = pathInfoPtr->fileNames; fileInfoPtr != NULL; \
fileInfoPtr = fileInfoPtr->next )
{
if( matchString( fileInfoPtr->fileName, fileName, fileInfoPtr->hasWildcards ) && \
( ( flags & RECURSE_SUBDIR ) || \
matchString( path, filePath, pathInfoPtr->hasWildcards ) ) && \
( fileHdrCurrPtr->hType ) == commentType )
{
/* Match on fileName/path, select it */
fileHdrCurrPtr->tagged = TRUE;
/* Select the directories leading to it */
if( ( dirNo != ROOT_DIR ) && ( flags & STORE_PATH ) )
{
/* Mark the encompassing directory for later creation */
setDirTaggedStatus( dirNo );
/* Now walk up the tree to the root directory marking
every directory we pass through unless we're creating
only the containing directory */
if( !( dirFlags & DIR_CONTAIN ) )
while( ( dirNo = getParent( dirNo ) ) != ROOT_DIR && \
!getDirTaggedStatus( dirNo ) )
setDirTaggedStatus( dirNo );
}
/* We've matched the name, exit the match loop */
goto endNestedFileLoop;
}
}
}
endNestedFileLoop:
fileHdrCurrPtr = fileHdrCurrPtr->next;
}
/* Now make a second pass selecting directories. Unfortunately we
can't use the same early-out match scheme as before since there
may be multiple path spec to dir path matches, so we have to
check all path specs against the dir path */
for( dirNo = getFirstDir(); dirNo != END_MARKER; dirNo = getNextDir() )
{
/* Try and match the path to the directory */
filePath = getPath( dirNo );
if( dirNo != ROOT_DIR )
/* Stomp final SLASH on non-root dir */
filePath[ strlen( filePath ) - 1 ] = '\0';
for( pathInfoPtr = filePathListStart; pathInfoPtr != NULL; \
pathInfoPtr = pathInfoPtr->next )
{
/* Get path information, skipping the initial SLASH if necessary */
path = pathInfoPtr->filePath;
if( *path == SLASH )
path++;
hasMatch = FALSE;
if( matchString( path, filePath, pathInfoPtr->hasWildcards ) )
{
/* Try and match the dir.name and path against a given filespec */
for( fileInfoPtr = pathInfoPtr->fileNames; fileInfoPtr != NULL; \
fileInfoPtr = fileInfoPtr->next )
if( ( dirInfoPtr = getFirstDirEntry( dirNo ) ) != NULL )
do
if( matchString( fileInfoPtr->fileName, dirInfoPtr->dirName, \
fileInfoPtr->hasWildcards ) )
{
hasMatch = TRUE;
if( flags & RECURSE_SUBDIR )
/* Select all nested info */
selectDir( dirInfoPtr, TRUE );
else
if( fileInfoPtr->hasWildcards )
/* Select the directory itself */
setDirTaggedStatus( dirInfoPtr->dirIndex );
else
/* Select only immediate contents */
selectDirNoContents( dirInfoPtr->dirIndex, TRUE );
}
while( ( dirInfoPtr = getNextDirEntry() ) != NULL );
/* The directory matched but there were no matching files
within it, select the directory itself. This occurs if
the form /dir/ is used instead of /dir, to override the
file selection */
if( !hasMatch )
setDirTaggedStatus( dirNo );
}
}
}
/* If we're asked to create all paths, mark all directories as selected */
if( dirFlags & DIR_ALLPATHS )
{
getFirstDir(); /* Skip archive root directory */
for( dirNo = getNextDir(); dirNo != END_MARKER; dirNo = getNextDir() )
setDirTaggedStatus( dirNo );
}
/* If we're in unified compression mode, remember the last tagged file
header. This is useful since it allows an early exit from the block
decompression when individual files are being extracted */
if( flags & BLOCK_MODE )
for( fileHdrCurrPtr = fileHdrStartPtr; fileHdrCurrPtr != NULL; \
fileHdrCurrPtr = fileHdrCurrPtr->next )
if( fileHdrCurrPtr->tagged )
endPtr = fileHdrCurrPtr;
#ifndef GUI
/* Simple case: Display selected files/directories and exit */
if( choice == VIEW )
{
listFiles();
return;
}
#endif /* !GUI */
/* Recreate the directory tree in the archive if necessary */
if( ( choice == EXTRACT ) && ( flags & STORE_PATH ) )
makeDirTree();
/* Flag the fact that we want to overwrite existing files headers rather
than add new ones when we call addData() */
overWriteEntry = TRUE;
/* Force a read the first time we call getByte() */
forceRead();
/* Process the encryption header if there is one */
if( ( choice == EXTRACT || choice == TEST || choice == DISPLAY ) && \
cryptFlags & ( CRYPT_CKE_ALL | CRYPT_PKE_ALL ) )
{
if( !cryptSetInput( getCoprDataLength(), &dummy ) )
error( CANNOT_PROCESS_CRYPT_ARCH );
/* Skip the dummy header which covers the encryption info */
prevPtr = fileHdrCurrPtr = fileHdrStartPtr->next;
}
else
prevPtr = fileHdrCurrPtr = fileHdrStartPtr;
skipDist = 0L;
again:
while( fileHdrCurrPtr != NULL )
{
processedFile = FALSE;
filePath = getPath( fileHdrCurrPtr->data.dirIndex );
if( fileHdrCurrPtr->tagged && fileHdrCurrPtr->hType == commentType )
{
if( choice == DELETE )
{
/* Ask for confirmation if necessary */
if( flags & INTERACTIVE )
if( confirmSkip( MESG_DELETE, buildInternalPath( fileHdrCurrPtr ), \
FALSE ) )
goto skipData;
archiveChanged = TRUE;
#ifndef GUI
hprintfs( MESG_DELETING_s_FROM_ARCHIVE, \
buildInternalPath( fileHdrCurrPtr ) );
#endif /* GUI */
skipDist += fileHdrCurrPtr->data.dataLen + \
fileHdrCurrPtr->data.auxDataLen;
#if 0 /* !!!! Currently unused !!!! */
/* Remove the header from the link chain */
if( fileHdrCurrPtr->nextLink == NULL )
{
if( fileHdrCurrPtr->prevLink != NULL )
{
/* Delete header from end of chain */
fileHdrCurrPtr->prevLink->nextLink = NULL;
goto skipData;
}
}
else
if( fileHdrCurrPtr->prevLink == NULL )
{
/* Delete header from start of chain */
fileHdrCurrPtr->nextLink->prevLink = NULL;
goto skipData;
}
else
{
/* Delete header from middle of chain */
fileHdrCurrPtr->nextLink->prevLink = NULL;
fileHdrCurrPtr->prevLink->nextLink = NULL;
goto skipData;
}
#endif /* 0 */
/* Remove the header for the skipped file from the header list */
if( fileHdrCurrPtr == fileHdrStartPtr )
{
/* Special case for first header */
prevPtr = fileHdrCurrPtr = fileHdrStartPtr->next;
deleteFileHeader( fileHdrStartPtr );
fileHdrStartPtr = fileHdrCurrPtr;
goto again;
}
else
{
prevPtr->next = fileHdrCurrPtr->next;
deleteFileHeader( fileHdrCurrPtr );
fileHdrCurrPtr = prevPtr;
}
/* Flag the fact that we've processed a file in this pass */
processedFile = TRUE;
}
else
if( choice == FRESHEN || choice == REPLACE )
{
/* Make sure the matched file is in the archive, that
there is another copy on disk to freshen from, and
that it's newer than the one in the archive */
if( addFileName( fileHdrCurrPtr->data.dirIndex, \
fileHdrCurrPtr->hType, \
fileHdrCurrPtr->fileName ) == FALSE )
goto endProcessFile;
if( !( flags & STORE_PATH ) && !*basePath && *filePath )
{
/* File has a path within the archive but no STORE_PATH
or explicit base path were given, create a false
base path corresponding to the internal path */
strcpy( basePath, filePath );
basePathLen = strlen( basePath );
falseBasePath = TRUE;
}
if( !findFirst( ( filePath = buildExternalPath( fileHdrCurrPtr ) ), \
( flags & STORE_ATTR ) ? ALLFILES : FILES, &fileInfo ) )
{
findEnd( &fileInfo );
goto endProcessFile;
}
findEnd( &fileInfo );
if( ( choice == FRESHEN ) && \
( fileInfo.fTime <= fileHdrCurrPtr->data.fileTime ) )
goto endProcessFile;
/* Ask for confirmation if necessary */
if( flags & INTERACTIVE )
if( confirmSkip( ( choice == FRESHEN ) ? MESG_FRESHEN : \
MESG_REPLACE, \
filePath, FALSE ) )
goto endProcessFile;
archiveChanged = TRUE;
/* Add the new header data onto the end of list2 */
if( list2startPtr == NULL )
/* Set up list2 if it's not already set up */
list2startPtr = fileHdrCurrPtr;
else
list2currPtr->next = fileHdrCurrPtr;
list2currPtr = fileHdrCurrPtr;
/* Add the new data to the archive */
freshenFD = hopen( filePath, O_RDONLY | S_DENYWR | A_SEQ );
setInputFD( freshenFD );
addData( filePath, &fileInfo, fileHdrCurrPtr->data.dirIndex, freshenFD );
hclose( freshenFD );
setInputFD( archiveFD );
/* If we're moving file, remember the name so we can go
back and delete it later */
if( flags & MOVE_FILES )
addFilePath( filePath );
/* Unlink the header for the file to be updated */
if( fileHdrCurrPtr == fileHdrStartPtr )
{
/* Special case for first header */
prevPtr = fileHdrCurrPtr = fileHdrStartPtr->next;
fileHdrStartPtr = fileHdrCurrPtr;
goto again;
}
else
{
prevPtr->next = fileHdrCurrPtr->next;
fileHdrCurrPtr = prevPtr;
}
/* Flag the fact that we've processed a file in this pass */
processedFile = TRUE;
}
else
{
/* Extract data */
skipToData();
retrieveData( fileHdrCurrPtr );
/* If there are no more files to decompress, exit now
(useful if BLOCK_MODE is set since we don't need to
process any data after this point) */
if( fileHdrCurrPtr == endPtr )
break;
}
}
endProcessFile:
/* Reset the false base path if necessary */
if( falseBasePath )
{
*basePath = '\0';
basePathLen = 0;
falseBasePath = FALSE;
}
/* Check if there was a match for this file, and if we haven't
already processed a file. The second check is necessary since
we may have deleted a file header as part of the operation
performed, and fileHdrCurrPtr now points at another file header */
if( !fileHdrCurrPtr->tagged && !processedFile )
if( choice == DELETE )
{
skipData:
skipToData();
moveData( fileHdrCurrPtr->data.dataLen + fileHdrCurrPtr->data.auxDataLen );
}
else
if( flags & BLOCK_MODE )
{
/* We're decompressing in block mode, we have to decompress
this file to keep the decompressor in sync, so we turn
the extract into a test */
choice = TEST;
skipToData();
retrieveData( fileHdrCurrPtr );
choice = EXTRACT;
}
else
/* Skip this archived file */
skipDist += fileHdrCurrPtr->data.dataLen + \
fileHdrCurrPtr->data.auxDataLen;
/* Step to next node in list */
prevPtr = fileHdrCurrPtr;
fileHdrCurrPtr = fileHdrCurrPtr->next;
}
/* Handle the archive update commands by moving any remaining data across
to the new archive, concatenating the header list for this data onto
the end of the one for the new archive, and then calling writeArcDir() */
if( choice == FRESHEN || choice == REPLACE )
{
/* Step through the list of headers copying the data across as we go */
for( fileHdrCurrPtr = fileHdrStartPtr; fileHdrCurrPtr != NULL; \
fileHdrCurrPtr = fileHdrCurrPtr->next )
{
hlseek( archiveFD, fileHdrCurrPtr->offset + HPACK_ID_SIZE, SEEK_SET );
moveData( fileHdrCurrPtr->data.dataLen + fileHdrCurrPtr->data.auxDataLen );
}
/* Concatenate the header list onto the end of the header list for the
new archive if there is one */
if( list2startPtr != NULL )
{
list2currPtr->next = fileHdrStartPtr;
fileHdrStartPtr = list2startPtr;
}
}
/* Perform cleanup for those functions that need it */
if( choice == DELETE || choice == FRESHEN || choice == REPLACE )
{
/* Flush the data buffer */
flushBuffer();
/* Delete the old archive and rename the temp file to the archive
name unless it's been deleted (which can happen if we delete all
the files in the archive) */
copyExtraInfo( archiveFileName, errorFileName );
hclose( archiveFD );
archiveFD = workFD;
writeArcDir();
hclose( archiveFD );
hunlink( archiveFileName );
if( errorFD != ERROR )
hrename( errorFileName, archiveFileName );
/* Flag the fact that we no longer need to attempt any recovery in
case of an error */
archiveFD = errorFD = dirFileFD = IO_ERROR;
}
else
if( choice == EXTRACT || choice == TEST )
/* If we were extracting or testing files, print a summary of
corrupted files (since these often scroll off the screen too
quickly to see) */
processBadFileInfo();
} /* "Real programmers can write five-page-long do loops
without difficulty" - _Real_Men_Don't_Program_Pascal_ */